package net.sf.ivyide.idea;

import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.application.Result;
import com.intellij.openapi.module.ModifiableModuleModel;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ModifiableRootModel;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.roots.impl.libraries.ProjectLibraryTable;
import com.intellij.openapi.roots.libraries.Library;
import com.intellij.openapi.roots.libraries.LibraryTable;

import java.util.LinkedHashMap;
import java.util.Map;

/**
 * Single object that tracks the complete ivy sync..
 */
public class SyncContext {

    private final LibraryTable.ModifiableModel projectLibraryTableModel;
    private final Project project;
    private final ModifiableModuleModel modulesModel;
    private final Map<Library, Library.ModifiableModel> libraryModels = new LinkedHashMap<Library, Library.ModifiableModel>();
    private final Map<Module, ModifiableRootModel> moduleModels = new LinkedHashMap<Module, ModifiableRootModel>();

    public SyncContext(final Project project) {
        this.project = project;
        projectLibraryTableModel = ProjectLibraryTable.getInstance(project).getModifiableModel();
        modulesModel = new ReadAction<ModifiableModuleModel>() {
                protected void run(Result<ModifiableModuleModel> result) throws Throwable {
                    result.setResult(com.intellij.openapi.module.ModuleManager.getInstance(project).getModifiableModel());
                }
            }.execute().getResultObject();
    }

    public LibraryTable.ModifiableModel getProjectLibraryTableModel() {
        return projectLibraryTableModel;
    }


    public ModifiableModuleModel getModulesModel() {
        return modulesModel;
    }

    public ModifiableRootModel getModuleModel(final Module module) {
        ModifiableRootModel result = moduleModels.get(module);
        if (result == null) {
            result = new ReadAction<ModifiableRootModel>() {
                protected void run(Result<ModifiableRootModel> result) throws Throwable {
                    result.setResult(ModuleRootManager.getInstance(module).getModifiableModel());
                }
            }.execute().getResultObject();
            moduleModels.put(module, result);
        }
        return result;
    }

    public Library.ModifiableModel getLibraryModel(Library library) {
        Library.ModifiableModel modifiableModel = libraryModels.get(library);
        if (modifiableModel == null) {
            modifiableModel = library.getModifiableModel();
            libraryModels.put(library, modifiableModel);
        }
        return modifiableModel;
    }

    public void commit() {
        for (Library.ModifiableModel modifiableModel : libraryModels.values()) {
            modifiableModel.commit();
        }
        projectLibraryTableModel.commit();

        ProjectRootManager.getInstance(project).multiCommit(modulesModel,
                moduleModels.values().toArray(new ModifiableRootModel[moduleModels.size()]));

    }
}
